#include "HIRCode.h"
#include "HXD1.h"
#include "HExceptions.h"
//#include "HVisitor.h"

/*
 * Scan the context of `ctx` and get the data.
 * For external variable (`is_external` is set), return a pointer to data
 * or `nullptr` if the data is all zero (zero initializer).
 * Otherwise, the return value is always valid pointer (for local variable).
 * `vdim` is the dimension of the variable that `ctx` is used to initialize.
 * NOTE: the `ctype` of `ctx` must be set.
 * Other parameters are used for recursion:
 * `data`: pointer to the array that will be filled;
 * `offset`: filling will start from here (the unit is element rather than byte);
 * `level`: recursion level.
 */
void * get_data_of_const_init_val(CACTParser::Const_init_valContext *ctx,
	const std::vector<uint32_t> &vdim, bool is_external,
	void *data, size_t offset, uint32_t level)
{
	// size of the defined variable, only set for `level==0u`
	size_t total_size = 1u;

	if (level == 0u)
	{	// if it is called the first time, allocate memory
		assert(offset == 0u && data == nullptr);

		for (auto d : vdim)
			total_size *= d;
		total_size *= cactsize(ctx->ctype.base_type);
		assert(total_size != 0u);
		total_size = roundup(total_size, 8u);
		data = new uint64_t[total_size / 8u];
		// initialize it with zero
		for (size_t i = 0u; i < total_size / 8u; ++i)
			((uint64_t*)(data))[i] = 0ul;
	}
	
	if (ctx->ctype.dim.size() == 0u)
	{	// for a scalar, just set it
		assert(ctx->const_expr() != nullptr);
		assert((level != 0u) || (offset == 0u));
		switch (ctx->ctype.base_type)
		{
		case Btype::INT:
			((int32_t*)(data))[offset] = ctx->const_expr()->number()->val.ival;
			break;
		case Btype::FLOAT:
			((float*)(data))[offset] = ctx->const_expr()->number()->val.fval;
			break;
		case Btype::DOUBLE:
			((double*)(data))[offset] = ctx->const_expr()->number()->val.dval;
			break;
		case Btype::BOOL:
			if (ctx->const_expr()->Bool_const()->getText() == "true")
				((char*)(data))[offset] = 1;
			break;
		default:
			assert(0);
			break;
		}
	}
	else if (ctx->ctype.dim.size() == 1u)
	{	// a flatten array
		uint32_t i;
		for (i = 0u; i < ctx->ctype.dim.at(0); ++i)
		{
			get_data_of_const_init_val(ctx->const_init_val(i), vdim,
				is_external, data, offset + i, level + 1u);
		}
	}
	else
	{	// recursion
		uint32_t i;
		size_t stride = 1u;	// size of child array
		for (i = level + 1u; i < vdim.size(); ++i)
			stride *= vdim.at(i);
		for (i = 0u; i < ctx->ctype.dim.at(0); ++i)
		{
			get_data_of_const_init_val(ctx->const_init_val(i), vdim,
				is_external, data, offset + i * stride, level + 1u);
		}
	}

	if (level > 0u || !is_external)
		return data;
	// if it is called the first time, `nullptr` may be returned for zero
	for (size_t i = 0u; i < total_size / 8u; ++i)
		if (((uint64_t*)(data))[i] != 0ul)
			return data;
	delete[] (uint64_t*)data;
	return nullptr;
}

/*
 * Generate the initialization code (`ALLOCA` and `STORE`)
 * for the local variable pointed by `phste` with the initial value `ti`.
 * `phste->pttn` will also be set.
 * This is for function parameters.
 * Return value is preserved for future use.
 */
/*
uint64_t HIRCode::generate_init_code(HSymbolTable *phsymtb, size_t hsteidx, HIRTempno ti)
{
	// Temp as pointer to the variable
	HIRTempno tp;
	HIRType tty;	// temp type
	const char *cominfo = nullptr;

	if (phsymtb->symbol(hsteidx).type->dim.size() > 0u)
		// Array is passed by pointer, and we don't
		// allocate memory for the pointer.
		return 1ul;
	tp = new_temp(HIRBtype::POINTER);

	// attach arg name in IR inst
	if (opts.debug_en != 0)
		cominfo = phsymtb->symbol(hsteidx).id.c_str();

	tty.length = 1u;
	tty.base_type = CACTBtype2HIRBtype(phsymtb->symbol(hsteidx).type->base_type);
	// make sure that the alignment is power of 2!
	insert_alloca(tp, tty, cactsize(phsymtb->symbol(hsteidx).type->base_type),
		cominfo);
	append_store(tty.base_type,
		HIROpnd{HIROpnd::TEMP, tp}, HIROpnd{HIROpnd::TEMP, ti});
	phsymtb->symbol(hsteidx).pttn = tp;
	// `latn` has been set in `add_funct()`
	phsymtb->symbol(hsteidx).latn = INVALID_TEMP_NO;
	return 0ul;
}
*/

/*
 * Generate the initialization code (`ALLOCA` and `STORE`)
 * for the local variable in `hsteidx`-th entry in `*phsymtb`
 * with the initial value `init_data`.
 * One of `latn` and `pttn` will be set.
 * This is for local variables including arraies.
 * If `init_data` is `nullptr`, it will be considered as not initialized.
 * Return value is preserved for future use.
 */
uint64_t HIRCode::generate_init_code(HSymbolTable *phsymtb, size_t hsteidx,
	const void *init_data)
{
	HIRType var_type{1u,
		CACTBtype2HIRBtype(phsymtb->symbol(hsteidx).type->base_type)};
	uint32_t base_size = cactsize(phsymtb->symbol(hsteidx).type->base_type);
	HIROpnd imm{HIROpnd::IMM};
	const char *cominfo = nullptr;

	assert(var_type.base_type != HIRBtype::UNKNOWN);

	imm.imm.ival = 0ll;	// clear the `imm` field

	// attach variable name in IR inst
	if (opts.debug_level != 0)
		cominfo = phsymtb->symbol(hsteidx).id.c_str();

	// calculate the total size (flattened)
	for (auto sz : phsymtb->symbol(hsteidx).type->dim)
		var_type.length *= sz;

	if (opts.no_alloc_stack == 0 || phsymtb->symbol(hsteidx).type->dim.size() > 0ul)
	{	/*
		 * For array or in the case of `-falloc-stack`, we allocate memory in the stack.
		 * The memory is initialized with the data in `init_data`.
		 * `pttn` will be valid.
		 * But we don't load it now, so `latn` will not be valid.
		 * Load it when it is used.
		 */
		// The pointer (as a temp) to the variable
		HIRTempno ptr_temp;
		// for `store` instruction
		HIROpnd ptr{HIROpnd::TEMP}, offset{HIROpnd::IMM},
			ptro{HIROpnd::TEMP};	// `ptr_temp` added by offset
		ptr_temp = new_temp(HIRBtype::POINTER);
		// generate `alloca`
		insert_alloca(ptr_temp, var_type, base_size, cominfo);
		// generate `store` code
		ptr.temp = ptr_temp;
		if (init_data != nullptr)
		{
			assert(elecpy(&imm.imm.ival, init_data,
				phsymtb->symbol(hsteidx).type->base_type) == 0);
			append_store(var_type.base_type, ptr, imm);
			// generate `add`, `store`, `add`, `store`, ...
			for (uint32_t idx = 1u; idx < var_type.length; ++idx)
			{
				ptro.temp = new_temp(HIRBtype::POINTER);
				offset.imm.ival = (int64_t)(idx * base_size);
				append_arlo(HIROper::ADD, ptro.temp, HIRBtype::POINTER, ptr, offset);
				elecpy(&imm.imm.ival, (int8_t*)init_data + idx * base_size,
					phsymtb->symbol(hsteidx).type->base_type);
				append_store(var_type.base_type, ptro, imm);
			}
		}
		phsymtb->symbol(hsteidx).latn = INVALID_TEMP_NO;
		phsymtb->symbol(hsteidx).pttn = ptr_temp;
	}
	else
	{	/*
		 * If optimization is enabled and the variable is scalar,
		 * we don't allocate memory but just use a temp to represent it.
		 * Works such as register allocation will be done by the backend.
		 */
		// temp representing the variable
		HIRTempno var_temp = new_temp(var_type.base_type);

		if (init_data != nullptr)
			assert(elecpy(&imm.imm.ival, init_data,
				phsymtb->symbol(hsteidx).type->base_type) == 0);
		else
			// init it with garbage
			imm.imm.ival = IMM_GARBAGE;
		// append garbage flag if `init_data` is `nullptr`
		append_arlo(HIROper::ADD, var_temp, var_type.base_type, imm, Zero,
			cominfo, init_data == nullptr);
		phsymtb->symbol(hsteidx).latn = var_temp;
		phsymtb->symbol(hsteidx).pttn = INVALID_TEMP_NO;
	}
	return 0ul;
}

/*
 * Generate IR code for expression and return the result (temp or imm).
 * For bool expression, if `as_cond` is set, this function will not
 * return a valid result but generate necessary branch instructions
 * towards `lbtrue` and `lbfalse`. Otherwise, the returned operand is valid.
 * For `ARR`, if `as_lval` is set, loading the element from memory will NOT
 * be done. A temp pointing to it will be returned in this case to support left value.
 */
HIROpnd HIRCode::generate_expr_code(const HExpression *expr, bool as_cond,
	HIRLabel lbtrue, HIRLabel lbfalse, bool as_lval)
{
	HExpression::Operation xoper;
	// invalid as default
	HIROpnd ret{HIROpnd::TEMP, INVALID_TEMP_NO};

	if (as_lval)
		// only variable and array reference can be lvalue
		assert(expr->oper() == HExpression::ARR ||
			(expr->oper() == HExpression::END && expr->psymtb() != nullptr));

	if (as_cond)
		// only bool expr can be conditinal expr
		assert(expr->etype().base_type == Btype::BOOL);
	else if (!as_lval && expr->etype().base_type == Btype::BOOL
		&& (expr->parent() == nullptr || expr->parent()->oper() != HExpression::ARR))
		// make sure that it is a scalar (array that completed addressed)
	{
		// generate code for bool expression
		if ((expr->oper() == HExpression::END && expr->psymtb() == nullptr)
			|| expr->flag_evaluated == true)
		{	// for a bool const, it is easy
			ret.tp = HIROpnd::IMM;
			ret.imm.ival = (expr->_bval ? 1l : 0l);
		}
		else
		{	// use `as_cond` being `true` to implement
			HIRLabel lbtrue = new_temp(HIRBtype::VOID),
				lbfalse = new_temp(HIRBtype::VOID);
			HIROpnd optrue{HIROpnd::IMM};
			const HIROpnd & opfalse = Zero;
			optrue.imm.ival = 1l;
			ret.temp = new_temp(HIRBtype::I8);
			// first set `%ret.temp` to zero
			append_arlo(HIROper::ADD, ret.temp, HIRBtype::I8, opfalse, opfalse);
			generate_expr_code(expr, true, lbtrue, lbfalse);
			append_label(lbtrue);
			// if true, add it by one
			append_arlo(HIROper::ADD, ret.temp, HIRBtype::I8, optrue, opfalse);
			append_label(lbfalse);
			// otherwise, just continue
		}
		return ret;
	}

	/*
	 * If the expression is NOT as left value and its
	 * value has been evaluated, just return or generate branch instruction.
	 */
	if (!as_lval && expr->flag_evaluated)
	{
		if (expr->etype().base_type != Btype::BOOL)
		{
			ret.tp = HIROpnd::IMM;
			ret.imm.ival = expr->_ival;
		}
		else
		{	// bool const
			if (expr->_bval == true)
				append_br(lbtrue);
			else
				append_br(lbfalse);
		}
		return ret;
	}

	switch (xoper = expr->oper())
	{
	// normal binary operations
	case HExpression::ADD: case HExpression::SUB: case HExpression::MUL:
	case HExpression::DIV: case HExpression::MOD:
	{
		HIRTempno dest = new_temp(CACTBtype2HIRBtype(expr->etype().base_type));
		HIROpnd op1, op2;
		HIROper irop;
		op1 = generate_expr_code(expr->lchild());
		op2 = generate_expr_code(expr->rchild());
		if (expr->etype().base_type == Btype::FLOAT ||
			expr->etype().base_type == Btype::DOUBLE)
			irop = (xoper == HExpression::ADD ? HIROper::FADD :
				xoper == HExpression::SUB ? HIROper::FSUB :
				xoper == HExpression::MUL ? HIROper::FMUL :
				xoper == HExpression::DIV ? HIROper::FDIV :
				HIROper::LABEL	// an error
			);
		else
			irop = (xoper == HExpression::ADD ? HIROper::ADD :
				xoper == HExpression::SUB ? HIROper::SUB :
				xoper == HExpression::MUL ? HIROper::MUL :
				xoper == HExpression::DIV ? HIROper::SDIV :
				xoper == HExpression::MOD ? HIROper::SREM :
				HIROper::LABEL	// an error
			);
		assert(irop != HIROper::LABEL);
		append_arlo(irop, dest,
			CACTBtype2HIRBtype(expr->etype().base_type), op1, op2
		);
		return HIROpnd{HIROpnd::TEMP, dest};
	}
		break;
	case HExpression::LOR:
	{	// `as_cond` must be set
		HIRLabel rlbf = new_temp(HIRBtype::VOID);	// right label for false
		generate_expr_code(expr->lchild(), true, lbtrue, rlbf);
		append_label(rlbf);
		generate_expr_code(expr->rchild(), true, lbtrue, lbfalse);
		return ret;
	}
		break;
	case HExpression::LAND:
	{
		HIRLabel llbt = new_temp(HIRBtype::VOID);	// left label for true
		generate_expr_code(expr->lchild(), true, llbt, lbfalse);
		append_label(llbt);
		generate_expr_code(expr->rchild(), true, lbtrue, lbfalse);
		return ret;
	}
		break;
	case HExpression::NOT:
	{
		generate_expr_code(expr->lchild(), true, lbfalse, lbtrue);
		return ret;
	}
		break;
	case HExpression::EQU: case HExpression::NEQ:
	case HExpression::LST: case HExpression::LEQ:
	case HExpression::GRT: case HExpression::GEQ:
	{
		HIRTempno dest = new_temp(HIRBtype::I8);
		HIROpnd op1, op2;
		ICMPCond icond;
		FCMPCond fcond;
		HExpression *lchild = expr->lchild();

		assert(lchild->etype().base_type == expr->rchild()->etype().base_type);

		op1 = generate_expr_code(lchild);
		op2 = generate_expr_code(expr->rchild());
		if (lchild->etype().base_type == Btype::FLOAT ||
			lchild->etype().base_type == Btype::DOUBLE)
		{
			fcond = (xoper == HExpression::EQU ? FCMPCond::OEQ :
				xoper == HExpression::NEQ ? FCMPCond::ONE :
				xoper == HExpression::LST ? FCMPCond::OLT :
				xoper == HExpression::LEQ ? FCMPCond::OLE :
				xoper == HExpression::GRT ? FCMPCond::OGT :
				FCMPCond::OGE
			);
			append_fcmp(dest, CACTBtype2HIRBtype(lchild->etype().base_type),
				fcond, op1, op2);
		}
		else
		{
			assert(lchild->etype().base_type == Btype::INT ||
				lchild->etype().base_type == Btype::BOOL);
			icond = (xoper == HExpression::EQU ? ICMPCond::EQ :
				xoper == HExpression::NEQ ? ICMPCond::NE :
				xoper == HExpression::LST ? ICMPCond::SLT :
				xoper == HExpression::LEQ ? ICMPCond::SLE :
				xoper == HExpression::GRT ? ICMPCond::SGT :
				ICMPCond::SGE
			);
			append_icmp(dest, CACTBtype2HIRBtype(lchild->etype().base_type),
				icond, op1, op2);
		}
		append_br(dest, lbtrue, lbfalse);
		return ret;
	}
		break;
	case HExpression::ARR:
	{
		// Current offset (up to this dimension)
		HIROpnd offset_cur;
		// Offset of this level
		HIROpnd offset_this;
		// Index of this level (`rchild()`)
		HIROpnd index_this;
		// Total offset up to last dimension
		HIROpnd offset_last;

		bool lchild_is_ARR = (expr->lchild()->oper() == HExpression::ARR);
		if (lchild_is_ARR)
			offset_last = generate_expr_code(expr->lchild());
		HIROpnd ele_size_opnd{HIROpnd::IMM};
		ele_size_opnd.imm.ival = cactsize(expr->etype().base_type);
		for (auto sz : expr->etype().dim)
			// calculate the size of the element at this level
			ele_size_opnd.imm.ival *= (int64_t)sz;
		/*
		 * We use 32-bit signed integer to calculate the offset,
		 * so the size cannot be too large!
		 */
		if (opts.debug_level != 0)
			if (ele_size_opnd.imm.ival > (INT32_MAX >> 6))
				throw UnsupportedSituation("Array size to large: " +
					std::to_string(ele_size_opnd.imm.ival));

		index_this = generate_expr_code(expr->rchild());
		if (index_this.tp == HIROpnd::IMM)
		{	/*
			 * If the index of this level is const number,
			 * There is no need to generate a `MUL` instruction.
			 */
			offset_this.tp = HIROpnd::IMM;
			offset_this.imm.ival = ele_size_opnd.imm.ival * index_this.imm.ival;
		}
		else
		{
			offset_this.tp = HIROpnd::TEMP;
			offset_this.temp = new_temp(HIRBtype::I32);
			append_arlo(HIROper::MUL, offset_this.temp, HIRBtype::I32,
				index_this, ele_size_opnd
			);
		}

		if (lchild_is_ARR)
		{	// Calculate `offset_cur`
			if (offset_this.tp == HIROpnd::IMM && offset_this.imm.ival == 0l)
				offset_cur = offset_last;
			else if (offset_last.tp == HIROpnd::IMM && offset_this.tp == HIROpnd::IMM)
			{
				offset_cur.tp = HIROpnd::IMM;
				offset_cur.imm.ival = offset_last.imm.ival + offset_this.imm.ival;
			}
			else
			{
				offset_cur.tp = HIROpnd::TEMP;
				offset_cur.temp = new_temp(HIRBtype::I32);
				append_arlo(HIROper::ADD, offset_cur.temp, HIRBtype::I32,
					offset_last, offset_this);
			}
		}
		else
		// If it is the first level, `offset_cur` is equal to `offset_this`.
			offset_cur = offset_this;

		if (expr->parent() == nullptr || (
				expr->parent()->oper() != HExpression::ARR ||
				/*
				 * Fix: The left child of `expr`'s parent not being `expr` itself
				 * also means that this is the root for the array reference,
				 * even if the parent is also `ARR`!
				 * Hint: consider `a[b[i]]`. `b[i]`'s parent is `ARR`.
				 */
				expr->parent()->lchild() != expr
			)
		)
		{	// it is the root for the array reference
			const HExpression* parr = expr;
			HSymbolTable *psymtb;
			size_t hsteidx;
			// the opnd representing the address of the array
			HIROpnd arnm;
			// pointer to the element (or sub array) we want to access
			HIROpnd ptr;
			for (; parr->oper() == HExpression::ARR; parr = parr->lchild())
				;
			// now `parr` points to the leaf node
			psymtb = parr->psymtb();
			if (psymtb == nullptr)
				throw std::logic_error("Cannot find symbol table");
			hsteidx = parr->hsteidx();
			if (psymtb->parent() == nullptr)
			{	// external variable
				arnm.tp = HIROpnd::XVAR;
				arnm.xvaridx = search_extvar(psymtb->symbol(hsteidx).id);
			}
			else
			{	// local variable
				arnm.tp = HIROpnd::TEMP;
				arnm.temp = psymtb->symbol(hsteidx).pttn;
			}
			if ((arnm.tp == HIROpnd::XVAR && arnm.xvaridx == SIZE_MAX)
				|| (arnm.tp == HIROpnd::TEMP && arnm.temp == INVALID_TEMP_NO))
				throw std::logic_error("Cannot find array symbol");
			if (offset_cur.tp == HIROpnd::IMM && offset_cur.imm.ival == 0l)
				ptr = arnm;
			else
			{
				ptr.tp = HIROpnd::TEMP;
				ptr.temp = new_temp(HIRBtype::POINTER);
				append_arlo(HIROper::ADD, ptr.temp, HIRBtype::POINTER,
					arnm, offset_cur
				);
			}

			// only fully addressed array reference can be lvalue
			if (as_lval && expr->etype().dim.size() > 0ul)
				throw std::logic_error("Left value is not fully addressed array");

			if (expr->etype().dim.size() == 0u && !as_lval)
			{	// the array element has been located
				// load the element
				// for `lval`, don't load the element, neither.
				HIROpnd lddest{HIROpnd::TEMP};
				lddest.temp = 
					new_temp(CACTBtype2HIRBtype(expr->etype().base_type));
				if (ptr.tp == HIROpnd::TEMP)
					append_load(lddest.temp,
						CACTBtype2HIRBtype(expr->etype().base_type), ptr.temp);
				else
					append_load(lddest.temp,
						CACTBtype2HIRBtype(expr->etype().base_type), ptr.xvaridx);

				// if it is bool and `as_cond`, jump
				if (as_cond)
					append_br(lddest.temp, lbtrue, lbfalse);
				return lddest;
			}
			else
				// return the pointer to the element
				return ptr;
		}
		else
			return offset_cur;
	}
		break;
	case HExpression::FUNC:
	{
		std::vector<HIRFarg> *pfargs = nullptr;
		const HExpression *parg = expr;
		if (parg->lchild() != nullptr)
			for (pfargs = new std::vector<HIRFarg>; parg != nullptr; parg = parg->rchild())
			{	// generate code for each argument and store them
				HIRBtype argtype;
				if (parg->lchild()->etype().dim.size() > 0ul)
					// array is passed by pointer
					argtype = HIRBtype::POINTER;
				else
					argtype = CACTBtype2HIRBtype(parg->lchild()->etype().base_type);
				pfargs->push_back(HIRFarg{
					argtype,
					generate_expr_code(parg->lchild())
				});
			}
		if (expr->etype().base_type != Btype::VOID)
			ret.temp = new_temp(CACTBtype2HIRBtype(expr->etype().base_type));
			// otherwise `ret.temp` is invalid
		append_call(
			CACTBtype2HIRBtype(expr->etype().base_type),
			search_funct(functable.func(expr->hfteidx()).funcid),
			pfargs, ret.temp
		);
		return ret;
	}
		break;
	case HExpression::END:
	{
		if (expr->psymtb() == nullptr)
		{	// a const expression (number)
			if (expr->etype().base_type != Btype::BOOL)
			{
				ret.tp = HIROpnd::IMM;
				ret.imm.ival = expr->_ival;
			}
			else
			{	// bool const
				/*
				 * The situation that the expression is a bool const
				 * but not as condition has been dealt with at the beginning.
				 */
				if (expr->_bval == true)
					append_br(lbtrue);
				else
					append_br(lbfalse);
			}
		}
		else
		{
			HSymbolTable *psymtb = expr->psymtb();
			size_t hsteidx = expr->hsteidx();
			HIROpnd vptr;	// pointer to the variable
			if (psymtb->symbol(hsteidx).latn == INVALID_TEMP_NO)
			{	/*
				 * the value has not been loaded, so get its pointer first
				 * NOTE: for an array or external variable, `latn` must be invalid
				 */
				if (psymtb->parent() == nullptr)
				{	// external variable
					vptr.tp = HIROpnd::XVAR;
					vptr.xvaridx = search_extvar(psymtb->symbol(hsteidx).id);
				}
				else
				{	// the address must be valid as `alloca` is used
					vptr.tp = HIROpnd::TEMP;
					vptr.temp = psymtb->symbol(hsteidx).pttn;
					if (vptr.temp == INVALID_TEMP_NO ||
						temp(vptr.xvaridx).type != HIRBtype::POINTER)
						throw std::logic_error("Invalid variable pointer");
				}

				if (as_lval || expr->etype().dim.size() > 0ul)
				{	/*
					 * If `expr` is an array, just return its address. Why?
					 * Because only in function argument list can array appear as expr!
					 * Besides, if `as_lval`, also return the address.
					 */
					return vptr;
				}
				else
				{
					HIRBtype btype = CACTBtype2HIRBtype(expr->etype().base_type);
					ret.temp = new_temp(btype);
					if (vptr.tp == HIROpnd::TEMP)
						append_load(ret.temp, btype, vptr.temp);
					else	// `HIROpnd::XVAR`
						append_load(ret.temp, btype, vptr.xvaridx);
					//psymtb->symbol(hsteidx).latn = ret.temp;
					/*
					 * When optimization is not implemented, every access to a variable
					 * will cause memmory access. So we don't need to set `latn`.
					 * Otherwise, consider a situation:
					 * ```
					 * int a;
					 * if (...)
					 * 		a = 2;
					 * else
					 * 		... = a;
					 * ```
					 * If we set `latn` on `a = 2`, the `else` branch will
					 * get a wrong value of `a`!
					 */
				}
			}
			else
			{	/*
				 * The variable has been bound with a temp.
				 * NOTE: this situation only happens when optimization is enabled,
				 * and the variable is scalar.
				 */
				ret.tp = HIROpnd::TEMP;
				ret.temp = psymtb->symbol(hsteidx).latn;
			}
			if (as_cond)
				append_br(ret.temp, lbtrue, lbfalse);
		}
		return ret;
	}
		break;
	case HExpression::POS:
		return generate_expr_code(expr->lchild());
		break;
	case HExpression::NEG:
	{
		HIROpnd ori = generate_expr_code(expr->lchild());
		HIRBtype btype = CACTBtype2HIRBtype(expr->etype().base_type);
		ret.temp = new_temp(btype);
		if (btype == HIRBtype::DOUBLE || btype == HIRBtype::FLOAT)
			// use `fneg` for floating point
			append_fneg(ret.temp, btype, ori);
		else
		{
			assert(btype == HIRBtype::I32);
			HIROpnd zero{HIROpnd::IMM};
			zero.imm.ival = 0l;
			// use `sub` and zero for integer
			append_arlo(HIROper::SUB, ret.temp, btype, zero, ori);
		}
		return ret;
	}
		break;
	default:
		// Oops!
		throw std::logic_error("Invalid expression: " + std::to_string(xoper));
		return ret;
		break;
	}
}